package com.quest.space.poker.biz.impl;

import com.alibaba.fastjson.JSON;
import com.intellij.openapi.util.Pair;
import com.quest.space.data.WebSocketMsg;
import com.quest.space.poker.base.enums.PokerDecorEnum;
import com.quest.space.poker.base.enums.PokerTypeEnum;
import com.quest.space.poker.base.enums.PokerValueEnum;
import com.quest.space.poker.base.utils.PokerUtil;
import com.quest.space.poker.biz.PokerService;
import com.quest.space.poker.dto.PlayerDto;
import com.quest.space.poker.dto.PokerDto;
import com.quest.space.poker.dto.TableDto;
import com.quest.space.websocket.LocalMessageHandler;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.quest.space.poker.base.enums.PokerDecorEnum.SPADE;
import static com.quest.space.poker.base.enums.PokerValueEnum.BIG;
import static com.quest.space.poker.base.enums.PokerValueEnum.SMALL;
import static com.quest.space.poker.base.utils.PokerUtil.*;

public class PokerServiceImpl implements PokerService {

    @Override
    public List<List<PokerDto>> getPokers() {
        List<List<PokerDto>> list = new ArrayList<>();
        //初始化54张牌
        Map<Integer, PokerDto> pokers = initPokers();
        //从54中选3张底牌
        List<PokerDto> hand = splitPoker(pokers, 3);
        print(hand);
        list.add(hand);
        //从51中选17张牌
        List<PokerDto> one = splitPoker(pokers, 17);
        print(one);
        list.add(one);
        //从34中选17张牌
        List<PokerDto> two = splitPoker(pokers, 17);
        print(two);
        list.add(two);
        //剩余17张牌
        List<PokerDto> three = new ArrayList<>(pokers.values());
        print(three);
        list.add(three);
        return list;
    }

    @Override
    public void handleRobot(TableDto table, PlayerDto robot, PlayerDto player) {
        //如果上一手是队友出牌，直接pass，但是上一手如果是自己，需要继续出牌
        PlayerDto prePlayer = table.getPlayer(table.getPrePlayer());
        boolean isPass = false;
        if(!robot.getPlayerId().equals(prePlayer.getPlayerId())){
            if(robot.getType().equals(prePlayer.getType())){
                isPass = true;
            }
        }
        if (!isPass) {
            List<PokerDto> pokers = getRespondPokers(table, robot, player);
            if (CollectionUtils.isEmpty(pokers)) {
                //若pokers为空，说明当前电脑玩家pass，轮到下一个玩家或者电脑出牌
                sendRobotPassMsg(table, robot, player);
            }else {
                //若pokers不为空，将当前玩家出牌内容发送至客户端，轮到下一个玩家或者电脑出牌
                if(sendRobotMsg(table, robot, player, pokers)){
                    return;
                }
            }
        }else {
            sendRobotPassMsg(table, robot, player);
        }

        //下一个玩家出牌
        PlayerDto next = robot.getNext();
        if (next.getRobot()) {
            handleRobot(table, next, player);
        }
    }

    private void sendRobotPassMsg(TableDto table, PlayerDto robot, PlayerDto player) {
        WebSocketMsg msg = new WebSocketMsg();
        table.setShowPokers(robot.getPlayerId(), "pass");
        table.setAction(0);
        System.out.println("机器人玩家：" +robot.getPlayerId() + ": pass");
        printTable(table);
        msg.setData(JSON.toJSONString(table));
        LocalMessageHandler.handleServerMsg(msg);
    }

    private boolean sendRobotMsg(TableDto table, PlayerDto robot, PlayerDto player, List<PokerDto> pokers) {
        //若pokers不为空，将当前玩家出牌内容发送至客户端，轮到下一个玩家或者电脑出牌
        WebSocketMsg msg = new WebSocketMsg();
        table.setAction(1);
        table.setPrePlayer(robot.getPlayerId());
        table.setPrePokerList(pokers);
        String pokerStr = pokers.stream().map(PokerDto::getName).collect(Collectors.joining());
        table.setPrePoker(pokerStr);
        table.setPrePokerType(PokerTypeEnum.getPokerType(pokerStr));
        table.setShowPokers(robot.getPlayerId(), pokerStr);
        System.out.println("机器人：" + robot.getPlayerId() + " 出牌：" + pokerStr);
        printTable(table);
        msg.setData(JSON.toJSONString(table));
        LocalMessageHandler.handleServerMsg(msg);
        //判断电脑玩家剩余牌数
        if(CollectionUtils.isEmpty(table.getAllPokers(robot.getPlayerId()))){
            System.out.println("游戏结束，胜利方：农民");
            WebSocketMsg msg1 = new WebSocketMsg();
            table.setOver(true);
            table.setWinner(robot.getPlayerId());
            printTable(table);
            msg1.setData(JSON.toJSONString(table));
            LocalMessageHandler.handleServerMsg(msg1);
            return true;
        }
        return false;
    }




    private static Map<Integer, PokerDto> initPokers() {
        Map<Integer, PokerDto> pokers = new HashMap<>();
        Integer num = 0;
        for (PokerValueEnum p : PokerValueEnum.values()) {
            if (p.equals(BIG) || p.equals(SMALL)) {
                continue;
            }
            for (PokerDecorEnum t : PokerDecorEnum.values()) {
                num ++;
                pokers.put(num, new PokerDto(p.getName(), p.getValue(), t.getValue(), t.getDesc()));
            }
        }
        pokers.put(++num, new PokerDto(BIG.getName(), BIG.getValue(), SPADE.getValue(), SPADE.getDesc()));
        pokers.put(++num,new PokerDto(SMALL.getName(), SMALL.getValue(), SPADE.getValue(), SPADE.getDesc()));
        return pokers;
    }

    private static List<PokerDto> splitPoker(Map<Integer, PokerDto> poker, Integer num) {
        List<Integer> list = PokerUtil.getRandomList(new ArrayList<>(poker.keySet()), num);
        List<PokerDto> res = list.stream().map(poker::get).collect(Collectors.toList());
        //从map中移除已经选中的牌
        list.forEach(poker::remove);
        res.sort(Comparator.comparingInt(PokerDto::getValue));
        return res;
    }

    public static List<PokerDto> getRespondPokers(TableDto table, PlayerDto robot, PlayerDto player){
        //需要判断是跟牌还是自己出牌
        String playerId = robot.getPlayerId();
        //出牌
        System.out.println("robot-> preplayerId: " + table.getPrePlayer() + " robot: " + playerId + " preType: " + table.getPrePokerType().getDesc());
        if (playerId.equals(table.getPrePlayer())) {
            //如果说上一个出牌的人是自己，那么则说明轮完一圈没人跟牌，自己再随机出一张牌
            Pair<PokerTypeEnum, List<PokerDto>> pair = robotShowPoker(table, playerId);
            List<PokerDto> pokers = pair.getSecond();
            //移除要出的牌
            remove(pokers, table.getAllPokers(playerId), table, playerId);
            System.out.println("机器人" + playerId + "随机出牌: " + pokers.stream().map(PokerDto::getName).collect(Collectors.joining()));
            return pokers;
        }
        //跟牌
        List<PokerDto> allPoker = table.getAllPokers(playerId);
        List<PokerDto> poker = table.getPrePokerList();
        PokerTypeEnum type = table.getPrePokerType();
        int value = poker.stream().mapToInt(PokerDto::getValue).sum();
        switch (type) {
            case X:
                return getX(allPoker, value, table, playerId);
            case XX:
                return getXX(allPoker, value, table, playerId);
            case XXX:
                return getXXX(allPoker, value, table, playerId);
            case XXX_Y:
                return getXXX_Y(allPoker, value, table, playerId);
            case SHUN:
                return getShunZi(allPoker, poker, table, playerId);
            case BOMB:
                return getBomB(allPoker, value, table, playerId);
            default:
                return null;
        }

    }

    /**
     * 机器人主动出牌
     * @param table
     * @param playerId
     * @return
     */
    private static Pair<PokerTypeEnum,List<PokerDto>> robotShowPoker(TableDto table, String playerId) {
        //按照飞机—>顺子->对子->单牌，先手不想先出炸弹
        Map<Integer, List<PokerDto>> map;
        List<PokerDto> allPoker = table.getAllPokers(playerId);
        //如果有两个王，则把两个王提出来
        List<PokerDto> kings = allPoker.stream().filter(a -> a.getValue() >= SMALL.getValue()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(kings) && kings.size() == 2) {
            allPoker = allPoker.stream().filter(a -> a.getValue() < SMALL.getValue()).collect(Collectors.toList());
        }
        map = allPoker.stream().collect(Collectors.groupingBy(PokerDto::getValue));

        List<PokerDto> x = new ArrayList<>();
        List<List<PokerDto>> xx = new ArrayList<>();
        List<List<PokerDto>> xxx = new ArrayList<>();
        List<List<PokerDto>> xxxx = new ArrayList<>();
        for (Integer v : map.keySet()) {
            List<PokerDto> list = map.get(v);
            if (list.size() == 1) {
                x.addAll(list);
            }
            if (list.size() == 2) {
                xx.add(list);
            }
            if (list.size() == 3) {
                xxx.add(list);
            }
            if (list.size() == 4) {
                xxxx.add(list);
            }
        }

        PokerTypeEnum typeEnum = null;
        List<PokerDto> send = randomShunZi(allPoker, map);

        if(!CollectionUtils.isEmpty(send)){
            typeEnum = PokerTypeEnum.SHUN;
        }else if (!CollectionUtils.isEmpty(xxx)) {
            send = randomXXX_Y(x, xxx);
            if (!CollectionUtils.isEmpty(send)) {
                typeEnum = PokerTypeEnum.XXX_Y;
            }else {
                send = xxx.stream().findFirst().get();
                typeEnum = PokerTypeEnum.XXX;
            }
        } else if (!CollectionUtils.isEmpty(xx)) {
            send = xx.stream().findFirst().get();
            typeEnum = PokerTypeEnum.XX;
        }else if (!CollectionUtils.isEmpty(xxxx)) {
            send = xxxx.stream().findFirst().get();
            typeEnum = PokerTypeEnum.BOMB;
        } else if (!CollectionUtils.isEmpty(x)) {
            send.add(x.get(0));
            typeEnum = PokerTypeEnum.X;
        }else if (!CollectionUtils.isEmpty(kings)) {
            send = kings;
            typeEnum = PokerTypeEnum.BOMB_KING;
        }
        return new Pair<>(typeEnum, send);
    }


    private static List<PokerDto> randomXXX_Y(List<PokerDto> x, List<List<PokerDto>> xxx) {
        List<PokerDto> out = new ArrayList<>();
        if (CollectionUtils.isEmpty(xxx) || CollectionUtils.isEmpty(x)) {
            return out;
        }
        out.addAll(xxx.stream().findFirst().get());
        out.add(x.get(0));
        return out;
    }

    private static List<PokerDto> randomShunZi(List<PokerDto> allPoker, Map<Integer, List<PokerDto>> map) {
        //给定：34556779XJQKA中，找出一组最小的顺子，34567，9XJQKA，第一组即为最小顺子
        //1.map集合已经按照value进行分组，对Key进行排序；2.遍历ketSet，判断是否存在连续的数字
        allPoker.sort(Comparator.comparingInt(PokerDto::getValue));
        List<Integer> keys = new ArrayList<>(map.keySet()).stream()
                .filter(k -> k < PokerValueEnum.TWO.getValue())
                .sorted(Comparator.comparingInt(Integer::intValue))
                .collect(Collectors.toList());
        List<PokerDto> out = new ArrayList<>();
        List<Integer> shunzi = new ArrayList<>();
        int last = -1;
        for (int i = 0; i < keys.size(); i++) {
            int v = keys.get(i);
            if (last > 0) {
                if (v == last + 1) {
                    if(shunzi.size() >= 5){
                        break;
                    }
                    shunzi.add(v);
                }else {
                    shunzi.clear();
                }
            }
            last = v;
        }
        if (shunzi.size() < 5) {
            return out;
        }
        shunzi.forEach(k -> out.add(map.get(k).get(0)));
        return out;
    }

    private static List<PokerDto> getX(List<PokerDto> allPoker, int value, TableDto table, String playerId) {
        List<PokerDto> out = new ArrayList<>();
        for (PokerDto p : allPoker) {
            if (p.getValue() > value) {
                PokerDto pokerDto = new PokerDto();
                BeanUtils.copyProperties(p, pokerDto);
                out.add(pokerDto);
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        return out;
    }

    private static List<PokerDto> getXX(List<PokerDto> allPoker, int value, TableDto table, String playerId) {
        List<PokerDto> out = new ArrayList<>();
        Map<Integer, List<PokerDto>> map = allPoker.stream().collect(Collectors.groupingBy(PokerDto::getValue));

        List<List<PokerDto>> xx = new ArrayList<>();
        List<List<PokerDto>> xxx = new ArrayList<>();
        List<List<PokerDto>> xxxx = new ArrayList<>();
        for (Integer v : map.keySet()) {
            List<PokerDto> list = map.get(v);
            if (list.size() == 2) {
                xx.add(list);
            }
            if (list.size() == 3) {
                xxx.add(list);
            }
            if (list.size() == 4) {
                xxxx.add(list);
            }
        }
        if (!CollectionUtils.isEmpty(xx)) {
            Optional<List<PokerDto>> opxx = xx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value).findFirst();
            if (opxx.isPresent()) {
                remove(opxx.get(), allPoker, table, playerId);
                return opxx.get();
            }
        }
        if (!CollectionUtils.isEmpty(xxx)) {
            Optional<List<PokerDto>> opxxx = xxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 1.5).findFirst();
            if (opxxx.isPresent()) {
                out = opxxx.get().subList(0, 2);
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        if (!CollectionUtils.isEmpty(xxxx)) {
            Optional<List<PokerDto>> opxxxx = xxxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 2).findFirst();
            if (opxxxx.isPresent()) {
                out = opxxxx.get().subList(0, 2);
                remove(out, allPoker, table, playerId);
                return opxxxx.get();
            }
        }
        return out;
    }

    private static List<PokerDto> getXXX(List<PokerDto> allPoker, int value, TableDto table, String playerId) {
        List<PokerDto> out = new ArrayList<>();
        Map<Integer, List<PokerDto>> map = allPoker.stream().collect(Collectors.groupingBy(PokerDto::getValue));

        List<List<PokerDto>> xxx = new ArrayList<>();
        List<List<PokerDto>> xxxx = new ArrayList<>();

        for (Integer v : map.keySet()) {
            List<PokerDto> list = map.get(v);
            if (list.size() == 3) {
                xxx.add(list);
            }
            if (list.size() == 4) {
                xxxx.add(list);
            }
        }
        if (!CollectionUtils.isEmpty(xxx)) {
            Optional<List<PokerDto>> opxxx = xxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 1.5).findFirst();
            if (opxxx.isPresent()) {
                out = opxxx.get().subList(0, 3);
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        if (!CollectionUtils.isEmpty(xxxx)) {
            Optional<List<PokerDto>> opxxxx = xxxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 2).findFirst();
            if (opxxxx.isPresent()) {
                out = opxxxx.get().subList(0, 3);
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        return out;
    }

    private static List<PokerDto> getXXX_Y(List<PokerDto> allPoker, int value, TableDto table, String playerId) {
        List<PokerDto> out = new ArrayList<>();
        //如果有两个王，则把两个王提出来
        List<PokerDto> kings = allPoker.stream().filter(a -> a.getValue() >= SMALL.getValue()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(kings) && kings.size() == 2) {
            allPoker = allPoker.stream().filter(a -> a.getValue() < SMALL.getValue()).collect(Collectors.toList());
        }
        Map<Integer, List<PokerDto>> map = allPoker.stream().collect(Collectors.groupingBy(PokerDto::getValue));

        List<List<PokerDto>> a = new ArrayList<>();
        List<List<PokerDto>> xxx = new ArrayList<>();
        List<List<PokerDto>> xxxx = new ArrayList<>();
        for (Integer v : map.keySet()) {
            List<PokerDto> list = map.get(v);
            if (list.size() == 1) {
                a.add(list);
            }
            if (list.size() == 3) {
                xxx.add(list);
            }
            if (list.size() == 4) {
                xxxx.add(list);
            }
        }
        if (CollectionUtils.isEmpty(a)) {
            return out;
        }
        if (!CollectionUtils.isEmpty(xxx)) {
            Optional<List<PokerDto>> opxxx = xxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 1.5).findFirst();
            if (opxxx.isPresent()) {
                out.addAll(opxxx.get().subList(0, 3));
                out.add(a.get(0).get(0));
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        if (!CollectionUtils.isEmpty(xxxx)) {
            Optional<List<PokerDto>> opxxxx = xxxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 2).findFirst();
            if (opxxxx.isPresent()) {
                out.addAll(opxxxx.get().subList(0, 3));
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        return out;
    }

    private static List<PokerDto> getShunZi(List<PokerDto> allPoker, List<PokerDto> poker, TableDto table, String playerId) {
        //给定：34556779XJQK中 找出一组数大于34567，即9XJQK
        //1.去重:345678X；2，以4为最小数，找出连续的5个数；3，找到第一个即返回
        allPoker.sort(Comparator.comparingInt(PokerDto::getValue));
        poker.sort(Comparator.comparingInt(PokerDto::getValue));
        List<PokerDto> out = new ArrayList<>();
        Integer min = poker.get(0).getValue();
        Integer last = null;
        List<PokerDto> setPoker = allPoker.stream()
                .filter(a -> a.getValue() < PokerValueEnum.TWO.getValue())
                .filter(distinctPredicate(PokerDto::getName))
                .collect(Collectors.toList());
        for (int i = 0; i < setPoker.size(); i++) {
            PokerDto a = setPoker.get(i);
            int value = a.getValue();
            if (value <= min) {
                continue;
            }
            //如果上一个数不为空，且下一个数是不是连续递增的，则重新开始查找
            if (last != null && value != last + 1) {
                out.clear();
                min = value;//将最小值设置为当前数
                last = value;
                out.add(a);
                continue;
            }
            last = value;
            out.add(a);
            if (poker.size() == out.size()) {
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        return null;
    }

    private static List<PokerDto> getBomB(List<PokerDto> allPoker, int value, TableDto table, String playerId) {
        List<PokerDto> out = new ArrayList<>();
        Map<Integer, List<PokerDto>> map = allPoker.stream().collect(Collectors.groupingBy(PokerDto::getValue));
        List<List<PokerDto>> xxxx = new ArrayList<>();
        for (Integer v : map.keySet()) {
            List<PokerDto> list = map.get(v);
            if (list.size() == 4) {
                xxxx.add(list);
            }
        }
        if (!CollectionUtils.isEmpty(xxxx)) {
            Optional<List<PokerDto>> opxxxx = xxxx.stream().filter(x -> x.stream().mapToInt(PokerDto::getValue).sum() > value * 2).findFirst();
            if (opxxxx.isPresent()) {
                out = opxxxx.get().subList(0, 3);
                remove(out, allPoker, table, playerId);
                return out;
            }
        }else {//查找王炸
            List<PokerDto> list = allPoker.stream().filter(p -> p.getName().equalsIgnoreCase("W")).collect(Collectors.toList());
            if (list.size() == 2) {
                out = list;
                remove(out, allPoker, table, playerId);
                return out;
            }
        }
        return out;
    }



    public static  <K, T> Predicate<K> distinctPredicate(Function<K, T> function) {
        // 因为stream流是多线程操作所以需要使用线程安全的ConcurrentHashMap
        ConcurrentHashMap<T, Boolean> map = new ConcurrentHashMap<>();
        return t -> null == map.putIfAbsent(function.apply(t), true);
    }

    public static void main(String[] args) {
        List<Integer> keys = new ArrayList<>();
        keys.add(3);
        keys.add(4);
        keys.add(5);
        keys.add(7);
        keys.add(8);
        keys.add(9);
        keys.add(10);
        keys.add(12);
        keys.add(13);
        keys.add(14);
        List<Integer> shunzi = new ArrayList<>();
        int last = -1;
        for (int i = 0; i < keys.size(); i++) {
            int v = keys.get(i);
            if (last > 0) {
                if (v == last + 1) {
                    shunzi.add(v);
                }else {
                    if(shunzi.size() >= 5){
                        break;
                    }
                    shunzi.clear();
                }
            }
            last = v;
        }
        System.out.println("shunzi:" + JSON.toJSONString(shunzi));

        //test();
    }
}
